from flask import current_app
from flask import g
from flask import request
from flask import session

from info import constants, db
from info import user_login_data
from info.models import User, News, Category
from info.utils.image_storage import image_storage
from info.utils.response_code import RET
from . import admin_blue
from flask import render_template, jsonify, redirect
from datetime import datetime, timedelta
import time


# 增加修改分类编辑
# 访问路径：admin/add_category
# 请求方式：POST
# 请求参数：id,name
# 返回值：errno.errmsg
@admin_blue.route('/add_category', methods=['POST'])
def add_category():
    """
    功能描述：修改增加分类
    思路分析
        １．获取参数
        ２．校验参数
        ３．根据编号判断是增加还是编辑
    :return:
    """
    # １．获取参数
    c_id = request.json.get('id')
    c_name = request.json.get('name')
    # ２．校验参数
    if not c_name:
        return jsonify(errno=RET.DATAERR, errmsg='参数不全')
    # ３．根据编号判断是增加还是编辑
    if c_id:
        #  编号部位空，编辑
        try:
            category = Category.query.get(c_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='获取分类失败')
        # 判断分类对象是否存在
        if not category:
            return jsonify(errno=RET.NODATA, errmsg='分类不存在')
        # 分类对象存在修改值
        category.name = c_name
    else:
        #  创建新的对象
        category = Category()
        category.name = c_name
        try:
            #  添加到数据库
            db.session.add(category)
            db.session.commit()
        except Exception as e:
            current_app.logger.error(e)
            db.session.rollback()
            return jsonify(errno=RET.DBERR, errmsg='保存失败')
    # ４．返回响应
    return jsonify(errno=RET.OK, errmsg='操作成功')


# 新闻分类列表数据获取
# 请求路径：/admin/news_category
# 请求方式：GET
# 请求参数：无
# 返回值：返回数据，渲染页面
@admin_blue.route('/news_category')
def news_category():
    """
    功能描述：获取新闻分类列表
    思路分析：
        １．获取所有的信息分类
        ２．携带数据渲染页面
    :return:
    """
    # １．获取所有的信息分类
    try:
        categories = Category.query.all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询分类失败')
    # ２．携带数据渲染页面
    return render_template('admin/news_type.html', categories=categories)


# 新闻编辑详情
# 请求路径：/admin/news_edit_detail
# 请求方式：GET,POST
# 请求参数：GET:news_id, POST(news_id,title,digest,content,index_image,category_id)
# 返回值：GET:返回数据，渲染页面，POST:errno.errmsg
@admin_blue.route('/news_edit_detail', methods=['GET', 'POST'])
def news_edit_detail():
    """
    功能描述：新闻页的详情编辑
    思路分析：
        １．判断请求方式，如果是GET请求携带数据渲染页面
            １．１获取参数
            １．２校验参数
            １．３通过新闻id获取对象
            １．４判断新闻是否存在
            １．５携带数据，渲染页面
        ２．如果是POST请求修改数据，提交到数据库
            ２．１获取参数
            ２．２校验参数
            ２．３根据新闻编号获取新闻对象
            ２．４判断新闻是否存在
            ２．５修改新闻属性，并提交到数据库
            ２．６返回状态
    :return:
    """
    # １．判断请求方式，如果是GET请求携带数据渲染页面
    if request.method == 'GET':
        #     １．１获取参数
        news_id = request.args.get('news_id')
        #     １．２校验参数
        if not news_id:
            return jsonify(errno=RET.DATAERR, errmsg='参数不全')
        # １．３通过新闻id获取对象
        try:
            news = News.query.get(news_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='查询新闻异常')
        # １．４判断新闻是否存在
        if not news:
            return jsonify(errno=RET.NODATA, errmsg='新闻不存在')

        # 查询所有的分类信息
        try:
            categories = Category.query.all()
            categories.pop(0)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='获取分类信息失败')
        # １．５携带数据，渲染页面
        return render_template('admin/news_edit_detail.html', news=news.to_dict(), categories=categories)
    # ２．如果是POST请求修改数据，提交到数据库
    # ２．１获取参数
    news_id = request.form.get('news_id')
    title = request.form.get('title')
    digest = request.form.get('digest')
    content = request.form.get('content')
    index_image = request.files.get('index_image')
    category_id = request.form.get('category_id')
    # ２．２校验参数
    if not all([news_id, title, digest, content, index_image, category_id]):
        return jsonify(errno=RET.DATAERR, errmsg='参数不全')
    # ２．３根据新闻编号获取新闻对象
    try:
        news = News.query.get(news_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询新闻失败')
    # ２．４判断新闻是否存在
    if not news:
        return jsonify(errno=RET.NODATA, errmsg='新闻不存在')

    # 上传图片
    try:
        image_name = image_storage(index_image.read())
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='七牛云异常')

        # 判断是否上传成功
    if not image_name:
        return jsonify(errno=RET.DATAERR, errmsg='上传失败　')
    # ２．５修改新闻属性，并提交到数据库
    news.id = news_id
    news.title = title
    news.digest = digest
    news.content = content
    news.index_image_url = constants.QINIU_DOMIN_PREFIX + image_name
    news.category_id = category_id

    # ２．６返回状态
    return jsonify(errno=RET.OK, errmsg='操作成功')


# 新闻编辑列表＆模糊查询
# 请求路径：/admin/news_edit
# 请求方式：GET
# 请求参数：p
# 返回值：返回数据，渲染页面
@admin_blue.route('/news_edit')
def news_edit():
    """
    功能描述：获取新闻编辑列表
    思路分析：
        １．获取参数
        ２．参数类型转换
        ３．分页查询
        ４．获取分页查询对象，总页数，当前页数，当前页数对象
        ５．将当前页数对象转成字典列表
        ６．携带数据，渲染页面
    :return:
    """
    # １．获取参数
    page = request.args.get('p', '1')
    # ２．参数类型转换
    try:
        page = int(page)
    except Exception as e:
        page = 1

    # ３．分页查询
    try:
        paginate = News.query.filter(News.status == 0).order_by(News.create_time.desc()).paginate(page, 10, False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询失败')
    # ４．获取分页查询对象，总页数，当前页数，当前页数对象
    totalPage = paginate.pages
    currentPage = paginate.page
    items = paginate.items
    # ５．将当前页数对象转成字典列表
    news_list = []
    for news in items:
        news_list.append(news.to_dict())

    data = {
        'totalPage': totalPage,
        'currentPage': currentPage,
        'news_list': news_list
    }
    # ６．携带数据，渲染页面
    return render_template('admin/news_edit.html', data=data)


#  审核新闻详情
# 请求路径：/admin/news_review_detail
# 请求方式：POST, GET
# 请求参数：GET:news_id  POST:news_id,action
# 返回值：字典数据，渲染页面
@admin_blue.route('/news_review_detail', methods=['GET', 'POST'])
def news_review_detail():
    """
    功能描述：新闻审核详细页面
    思路分析：
        １．判断请求方式，如果是GET，渲染页面
        ２．获取参数
        ３．校验参数
        ４．根据信息编号获取新闻对象
        ５．判断判断新闻是否存在
        ６．返回数据，渲染页面
        ８．如果是POST请求，获取参数
        ９．校验参数
        １０．操作类型校验
        １１．通过新闻编号id获取新闻对象
        １２．判断新闻是否存在
        １３．根据操作类型，改变新闻状态
        １４．返回响应
    :return:
    """
    # １．判断请求方式，如果是GET，渲染页面
    if request.method == 'GET':

        # ２．获取参数
        news_id = request.args.get('news_id')
        # ３．校验参数
        if not news_id:
            return jsonify(errno=RET.DATAERR, errmsg='参数不全')
        # ４．根据信息编号获取新闻对象
        try:
            news = News.query.get(news_id)
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg='获取对象失败')
        # ５．判断判断新闻是否存在
        if not news:
            return jsonify(errno=RET.NODATA, errmsg='新闻不存在')
        # ６．返回数据，渲染页面
        return render_template('admin/news_review_detail.html', news=news.to_dict())
    # ８．如果是POST请求，获取参数
    news_id = request.json.get('news_id')
    action = request.json.get('action')
    # ９．校验参数
    if not all([news_id, action]):
        return jsonify(errno=RET.DATAERR, errmsg='参数错误')
    # １０．操作类型校验
    if action not in ['accept', 'reject']:
        return jsonify(RET.DATAERR, errmsg='操作类型错误')
    # １１．通过新闻编号id获取新闻对象
    try:
        news = News.query.get(news_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询新闻失败')
    # １２．判断新闻是否存在
    if not news:
        return jsonify(errno=RET.NODATA, errmsg='新闻不存在')
    # １３．根据操作类型，改变新闻状态
    if action == 'accept':
        news.status = 0
    else:
        reason = request.json.get('reason', '')
        news.reason = reason
        news.status = -1

    # １４．返回响应
    return jsonify(errno=RET.OK, errmsg='操作成功')


# 新闻审核列表和模糊查询
# 请求路径：/admin/user_review
# 请求方式：GET
# 请求参数：p,keywords(模糊查询)
# 返回值：返回字典数据，渲染页面
@admin_blue.route('/user_review')
def user_review():
    """
    功能描述：新闻审核列表＆模糊查询
    思路分析：
        １．获取参数
        ２．参数类型转换
        ３．分页查询
        ４．获取分页对象，总页数，当前页，当前页对象
        ５．将当前页对象转成字典列表
        ６．返回字典列表，渲染页面
    :return:
    """
    # １．获取参数
    page = request.args.get('p', '1')
    keywords = request.args.get('keywords')
    # ２．参数类型转换
    try:
        page = int(page)
    except Exception as e:
        page = 1
    # ３．分页查询
    try:
        #  判断是否写了搜索关键字
        filters = [News.status != 0]
        if keywords:
            filters.append(News.title.contains(keywords))
        paginate = News.query.filter(*filters).order_by(News.create_time.desc()).paginate(page, 10, False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询失败')
    # ４．获取分页对象，总页数，当前页，当前页对象
    totalPage = paginate.pages
    currentPage = paginate.page
    items = paginate.items
    # ５．将当前页对象转成字典列表
    news_list = []
    for news in items:
        news_list.append(news.to_review_dict())

    data = {
        'totalPage': totalPage,
        'currentPage': currentPage,
        'news_list': news_list
    }
    # ６．返回字典列表，渲染页面
    return render_template('admin/news_review.html', data=data)


#  用户列表
# 请求路径：admin/user_list
# 请求方式：GET
# 请求参数：p
# 返回值：返回字典，渲染页面
@admin_blue.route('/user_list')
def user_list():
    """
    功能描述：用户所有的用户列表
    思路分析：
        １．获取参数
        ３．参数类型转换
        ４．分页查询
        ５．获取分页对象数据，总页数，当前页，当前页对象
        ６．将对象列表转成字典列表
        ７．返回字典，渲染页面
    :return:
    """
    # １．获取参数
    page = request.args.get('p', '1')
    # 2．参数类型转换
    try:
        page = int(page)
    except Exception as e:
        page = 1
    # 3．分页查询
    try:
        paginate = User.query.filter(User.is_admin == False).order_by(User.create_time.desc()).paginate(page, 10, False)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询失败')
    # 4．获取分页对象数据，总页数，当前页，当前页对象
    totalPage = paginate.pages
    currentPage = paginate.page
    items = paginate.items
    # 5．将对象列表转成字典列表
    user_list = []
    for user in items:
        user_list.append(user.to_admin_dict())

    data = {
        'totalPage': totalPage,
        'currentPage': currentPage,
        'user_list': user_list
    }
    # 6．返回字典，渲染页面
    return render_template('admin/user_list.html', data=data)


# 管理员用户统计
# 请求路径：/admin/user_count
# 请求方式：GET
# 请求参数：无
# 返回值，携带数据，渲染页面
@admin_blue.route('/user_count')
def user_count():
    """
    功能描述：用户统计
    思路分析
        １．查询总人数，不包括管理员
        ２．查询月活人数
        ３．查询日活人数
        ４．查询时间段内活跃人数
        ５．携带数据，渲染页面
    :return:
    """
    # １．查询总人数，不包括管理员
    try:
        total_page = User.query.filter(User.is_admin == False).count()
    except Exception as e:
        current_app.logger.error(e)
        return render_template('admin/user_count.html', data='')
    # ２．查询月活人数
    calender = time.localtime()
    # time.struct_time(tm_year=2018, tm_mon=10, tm_mday=21, tm_hour=21, tm_min=3, tm_sec=40, tm_wday=6, tm_yday=294,
    #                  tm_isdst=0)

    try:
        #  获取到本月1号零点钟
        # 拼接成字符串
        month_starttime_str = '%d-%d-1' % (calender.tm_year, calender.tm_mon)  # 2018-10-1
        # 转成数字类型
        month_starttime_date = datetime.strptime(month_starttime_str, '%Y-%m-%d')

        #  获取此时的时间
        month_endtime_date = datetime.now()

        #  根据时间查询用户
        month_count = User.query.filter(User.last_login >= month_starttime_date, User.last_login <= month_endtime_date,
                                        User.is_admin == False).count()
    except Exception as e:
        current_app.logger.error(e)
        return render_template('admin/user_count.html', data='')
    # ３．查询日活人数
    try:
        #  获取到本月1号零点钟
        # 拼接成字符串
        day_starttime_str = '%d-%d-%d' % (calender.tm_year, calender.tm_mon, calender.tm_mday)  # 2018-10-21
        # 转成数字类型
        day_starttime_date = datetime.strptime(day_starttime_str, '%Y-%m-%d')

        #  获取此时的时间
        day_endtime_date = datetime.now()

        #  根据时间查询用户
        day_count = User.query.filter(User.last_login >= day_starttime_date, User.last_login <= day_endtime_date,
                                      User.is_admin == False).count()
    except Exception as e:
        current_app.logger.error(e)
        return render_template('admin/user_count.html', data='')
    # ４．查询时间段内活跃人数
    active_date = []  # 记录活跃日期
    active_count = []  # 记录活跃人数
    for i in range(0, 31):
        #  当天开始的时间
        begin_date = day_starttime_date - timedelta(days=i)
        #  当天开始的时间的后一天
        end_date = day_starttime_date - timedelta(days=i - 1)
        #  添加当天开始时间字符串到活跃日期中
        active_date.append(begin_date.strftime('%m-%d'))
        try:
            #  查询时间A-B这一天的活跃人数
            everyday_active_count = User.query.filter(User.is_admin == False, User.last_login >= begin_date,
                                                      User.last_login <= end_date).count()

        except Exception as e:
            current_app.logger.error(e)
            return render_template('admin/user_count.html', data='')

        # 添加当天活跃人数到列表中
        active_count.append(everyday_active_count)

    # 为了方便查看图表，反转
    active_date.reverse()
    active_count.reverse()
    # ５．携带数据，渲染页面
    data = {
        'total_page': total_page,
        'month_count': month_count,
        'day_count': day_count,
        'active_date': active_date,
        'active_count': active_count
    }
    return render_template('admin/user_count.html', data=data)


# 管理员首页
# 请求地址：admin/index
# 请求方式：GET
# 请求参数：无
# 返回值：管理员首页页面
@admin_blue.route('/index')
@user_login_data
def admin_index():
    return render_template('admin/index.html', admin=g.user.to_dict() if g.user else '')


# 管理员登界面
# 请求地址：admin/login
# 请求方式：POST，　GET
# 请求参数：GET　无参数　POST:username用户名,password密码
# 返回值：GET返回页面，POST返回errno,errmsg
@admin_blue.route('/login', methods=['POST', 'GET'])
def admin_login():
    """
    功能描述：管理员登陆
    思路分析：
        １．判断用户登陆方式，GET渲染页面
        ２．３如果是post请求获取参数
        ３．校验参数，非空校验
        ４．根据用户名查找管理员对象
        ５．判断管理员对象是否存在
        ６．校验密码是否正确
        ７．记录登陆信息到session
        ８．重定向到到首页
    :return:
    """
    # １．判断用户登陆方式，GET渲染页面
    if request.method == 'GET':
        # 判断管理员是否已经登陆过，已经登陆直接跳转到管理员首页
        if session.get('is_admin'):
            return redirect('/admin/index')
        return render_template('admin/login.html')
    # ２．３如果是post请求获取参数
    username = request.form.get('username')
    password = request.form.get('password')
    # ３．校验参数，非空校验
    if not all([username, password]):
        return jsonify(errno=RET.DATAERR, errmsg='参数错误')
    # ４．根据用户名查找管理员对象
    try:
        admin = User.query.filter(User.mobile == username, User.is_admin == True).first()
    except Exception as e:
        current_app.logger.error(e)
        return render_template('admin/login.html', errmsg='获取管理员失败')

    # ５．判断管理员对象是否存在
    if not admin:
        return render_template('admin/login.html', errmsg='管理员不存在')
    # ６．校验密码是否正确
    if not admin.check_passowrd(password):
        return render_template('admin/login.html', errmsg='密码错误')
    # ７．记录登陆信息到session
    session['user_id'] = admin.id
    session['nick_name'] = admin.nick_name
    session['mobile'] = admin.mobile
    session['is_admin'] = True
    # ８．重定向到到首页
    return redirect('/admin/index')
